#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using UTable.Config;
using UTable.Input;
using UTable.Input.MultiTouch;
using UTable.Input.Text;
using UTable.Logger;
using UTable.ObjectManager;
using UTable.Objects.Handlers.Gestures;
using UTable.Objects.Controls;

namespace UTable.Objects
{
    public abstract class UApplication : Application
    {
        #region Private Members

        private Window _window;
        private Grid _host;
        private IObject _root;

        #endregion

        #region Private Methods

        #region Initialization

        private void InitializeMainWindow()
        {
            // Create Window
            _window = new Window();
            _window.WindowStyle = WindowStyle.None;

            if (UTableHelper.Config.Window.Maximized)
                _window.WindowState = WindowState.Maximized;
            else
            {
                _window.Width = UTableHelper.Config.Window.Width;
                _window.Height = UTableHelper.Config.Window.Height;
                _window.Left = UTableHelper.Config.Window.Left;
                _window.Top = UTableHelper.Config.Window.Top;
                _window.Topmost = true;
            }

            _window.AllowsTransparency = true;
            _window.Background = new SolidColorBrush(Colors.Transparent);
        }

        #endregion

        #region Configurations

        private IInputProvider LoadInputProvider(InstanceConfig instance)
        {
            IInputProvider provider = instance.GetInstace() as IInputProvider;
            if (provider != null)
                provider.Initialize(this);
            SLogger.Info("Load InputProvider: " + provider.ToString());
            return provider;
        }

        /// <summary>
        /// Get the multi-touch providers for the platform
        /// This function will be called when the platform is starting up, 
        /// the default implementation is get the providers according to <see cref="UTableHelper.Config.Input.Providers"/>
        /// </summary>
        /// <returns></returns>
        protected virtual IInputProvider GetMultiTouchProvider()
        {
            if (UTableHelper.Config.Input.Providers.Count == 0)
                return null;
            else if (UTableHelper.Config.Input.Providers.Count == 1)
            {
                return LoadInputProvider(UTableHelper.Config.Input.Providers[0]);
            }
            else
            {
                CompositeProvider providers = new CompositeProvider();
                foreach (InstanceConfig i in UTableHelper.Config.Input.Providers)
                {
                    IInputProvider input = LoadInputProvider(i);
                    if (input != null)
                        providers.Providers.Add(input);
                }
                return providers;
            }
        }

        /// <summary>
        /// Get the type of the tabletop environment.
        /// This function will be called when the platform is starting up, and then the tabletop instance will be created.
        /// 
        /// If not override this function, this type is gained from the <see cref="UTableHelper.Config.Tabletop.Class.ClassType"/>
        /// </summary>
        /// <returns>the type of the tabletop environment</returns>
        protected virtual Type GetTabletopType()
        {
            return UTableHelper.Config.Tabletop.Class.ClassType;
        }

        /// <summary>
        /// Get the configuration for the whole platform.
        /// This function will be called when the application is starting up, and <see cref="UTableHelper.Config"/> will be set, 
        /// so that you can get the config from this global variable later.
        /// 
        /// If not override this function, a default configuration will be loaded
        /// </summary>
        /// <returns>The confiugration instance for the whole platform</returns>
        protected virtual UTableConfig GetConfig()
        {
            return ConfigGenerator.GetDefaultConifg();
        }

        #endregion

        #endregion

        #region Properties

        /// <summary>
        /// Get the <see cref="System.Windows.Window"/> for displaying the tabletop environment and applications on it.
        /// </summary>
        public new Window MainWindow
        {
            get { return _window; }
        }

        public Grid Host
        {
            get { return _host; }
        }

        /// <summary>
        /// Get the instance of the current tabletop environment
        /// </summary>
        public IObject Tabletop
        {
            get { return _root; }
        }

        #endregion

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            // load config and logger
            SLogger.Initialize();
            ULogger.Initialize();
            UTableHelper.Config = GetConfig();


            // start up ui tree host (in this thread)

            // build the object tree
            IObjectTree objectTreeHost = UObjectTreeHost.GetInstance();

            // show object tree
            InitializeMainWindow();
            _host = new Grid();
            MainWindow.Content = Host;
            MainWindow.Show();

            // Create input providers
            IInputProvider inputProvider = GetMultiTouchProvider();

            // create the input manager and object manager
            IInputManager inputManager = null;
            IGlobalObjectManager objectManager = null;

            if (UTableHelper.Config.MultiThread)
            {
                inputManager = new InputManagerImpl();
                objectManager = new GlobalObjectManagerImpl();
            }
            else
            {
                inputManager = new InputManagerImpl();
                objectManager = new SingleThreadObjectManager();
            }

            objectManager.InputManager = inputManager;
            objectManager.ObjectTree = UObjectTreeHost.GetInstance();

            UTableHelper.RegistgerGlobalObjectManager(objectManager);

            ObjectCreateParameter rootParam = new ObjectCreateParameter(GetTabletopType());
            _root = new UObjectEntity(rootParam);
            _root.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
            _root.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
            _root.Position = new Point(0, 0);
            _root.ObjectLoaded += new EventHandler(_root_ObjectLoaded);
            objectTreeHost.SetRoot(_root);
            Host.Children.Insert(0, objectTreeHost.HostElement);

            if (UTableHelper.Config.MultiThread)
            {
                inputManager.VisualTree = ObjectControlTree.GetInstance();
            }
            else
            {
                inputManager.VisualTree = (_root as UObjectEntity).Content;
            }

            (objectTreeHost as UObjectTreeHost).RegisterVisualTree(objectManager.InputManager);
            UTableHelper.RegisterInputProvider(typeof(MultiTouchEventArgs), inputProvider);
            UTableHelper.RegisterInputMapper(typeof(MultiTouchEventArgs), new MultiTouchInputMapper());
            
            // Create text input manager
            //TextInputManager textInputManager = new TextInputManager();
            //textInputManager.ObjectTree = UObjectTreeHost.GetInstance();
            //textInputManager.ObjectControlTree = ObjectControlTree.GetInstance();
            //UTableHelper.RegisterTextInputManager(textInputManager);

            // start up 
            inputManager.StartWork();
            objectManager.StartWork();
        }

        /// <summary>
        /// This function will be called when the whole platform started
        /// 
        /// The default implementation is empty, you can override this function to add your initialization code
        /// </summary>
        protected virtual void OnTabletopLoaded()
        {
            // do nothing here
        }

        private void _root_ObjectLoaded(object sender, EventArgs e)
        {
            OnTabletopLoaded();
        }
    }
}
